/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID coidgen tool.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Robo Strycek
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */





#include "coidgen.h"

#include <algorithm>




using namespace coid;



int H_FILE::compare( const H_FILE & old ) const
{
	int g_class_change = 0;
	if( cls.size() != old.cls.size() )
		g_class_change = cls.size() > old.cls.size() ? H_FILE::diff_class_added : H_FILE::diff_class_deleted;
	if( g_class_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
		return g_class_change;

	uint i;
	for( i=0; i<cls.size(); i++ ) {
		bool found = false;
		int class_change = 0;
		for( uint j=0; j<old.cls.size(); j++ ) {
			if( cls[i]->name == old.cls[i]->name ) {
				class_change = cls[i]->compare( *old.cls[i] );
				found = true;
				break;
			}
		}

		if( ! found )
			return H_FILE::diff_class_added;
		if( class_change ) {
			g_class_change |= class_change;
			if( g_class_change & 0xFFFF0000 ) {		/// if there is a major change ==> do not continue
				cls[i]->max_fnc_index = (uint)cls[i]->fnc_export.size();	/// restore max_fnc_index (may have been changed)
				return g_class_change;
			}
		}
	}



	return g_class_change;
}



int CLASS::compare( const CLASS & old )
{
	/// name is guaranteed to be the same

	int g_function_change = 0;
	if( (flags&f_major_change_mask) != (old.flags&f_major_change_mask) )
		return H_FILE::diff_class_changed_major;
	if( (flags&f_minor_change_mask) != (old.flags&f_minor_change_mask) )
		g_function_change |= H_FILE::diff_class_changed_minor;

	if( fnc_export.size() != old.fnc_export.size() )
		g_function_change = fnc_export.size() > old.fnc_export.size() ? H_FILE::diff_function_added : H_FILE::diff_function_deleted;
	if( g_function_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
		return g_function_change;




	/// compare connect functions:

	uint i;
	for( i=0; i<accept_connect_auto; i++ ) {
		int function_change = fnc_special[i]->compare( *old.fnc_special[i] );
		if( function_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
			return function_change;
	}



	/// compare exported functions:

	dynarray<int> old_added;
	old_added.add( fnc_export.size() );
	memset( old_added.ptr(), 0xFF, sizeof(int) * fnc_export.size() );


	/// we need to keep order, ids and count ==> build new dynarray:
	CLASS ctmp;		/// CLASS == easy way to auto destroy FUNCTION* at exit
	FUNCTION ** f = ctmp.fnc_export.add( old.fnc_export.size() );
	memset( f, 0, sizeof(FUNCTION*) * old.fnc_export.size() );


	//if( max_fnc_index < old.max_fnc_index )
	max_fnc_index = old.max_fnc_index;

	for( i=0; i<old.fnc_export.size(); i++, f++ ) {
		*f = new FUNCTION;
		**f = *(old.fnc_export[i]);
		(*f)->parent = this;

		bool found = false;
		int function_change = 0;
		int overloaded_count = 0;
		for( uint j=0; j<fnc_export.size(); j++ ) {
			if( (*f)->name == fnc_export[j]->name ) {
				found = true;
				overloaded_count++;
				function_change = (*f)->compare( *fnc_export[j] );

				if( function_change == 0 ) {	/// there may be a few functions with the same name
					uint id = (*f)->id;
					**f = *fnc_export[j];		/// copy names, ... from newer function declaration, keep id
					(*f)->id = id;
					old_added[j] = 0;
					break;
				}
			}
		}

		/// deleted method leads to major change recently
/*		if( overloaded_count > 1 && function_change ) { /// overloaded functions are hard to check for version changes ==>
												/// if it's not found in newer fnc set ==> lets say it's been deleted
			function_change = H_FILE::diff_function_deleted;
		}*/

		if( ! found ) {
			function_change |= H_FILE::diff_function_deleted;
			delete *f;
			*f = NULL;
		}

		g_function_change |= function_change;
		if( g_function_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
			return g_function_change;
	}


	/// no major change ==> merge all

	/// add new functions
	for( i=0; i<old_added.size(); i++ ) {
		if( old_added[i] ) {		/// there is a function, that was not found in the old class ==> added
			g_function_change |= H_FILE::diff_function_added;
			if( g_function_change & 0xFFFF0000 )
				return g_function_change;

			f = ctmp.fnc_export.add( 1 );
			*f = new FUNCTION;
			**f = *fnc_export[i];
			(*f)->parent = this;
			(*f)->id = (uint)max_fnc_index++;
		}
	}

	/// delete removed functions from array
	for( i=0; i<ctmp.fnc_export.size(); i++ ) {
		if( ctmp.fnc_export[i] == NULL ) {
			ctmp.fnc_export.del( i );
			i--;
		}
	}


	/// hack that swaps dynarrays
	void ** x1 = (void **) &ctmp.fnc_export;
	void ** x2 = (void **) &fnc_export;
	void * tmp = *x1; *x1 = *x2; *x2 = tmp;



	return g_function_change;
}










void CLASS::sort_functions_by( int what )
{
	if( what == 0 )
		std::sort( fnc_export.ptr(), fnc_export.ptr() + fnc_export.size(), FNC_SORT_BY_NAME() );
	else if( what == 1 )
		std::sort( fnc_export.ptr(), fnc_export.ptr() + fnc_export.size(), FNC_SORT_BY_ID() );
}



int FUNCTION::compare( const FUNCTION & old ) const
{
	int g_change = 0;
	if( args.size() != old.args.size() )
		g_change = args.size() > old.args.size() ? H_FILE::diff_arg_added : H_FILE::diff_arg_deleted;
	if( g_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
		return g_change;

	if( (flags&f_major_change_mask) != (old.flags&f_major_change_mask) )
		return H_FILE::diff_class_changed_major;
	if( (flags&f_minor_change_mask) != (old.flags&f_minor_change_mask) )
		g_change |= H_FILE::diff_class_changed_minor;

	/// check return value
	g_change |= ret.compare( old.ret );
	if( g_change & 0xFFFF0000 )		/// if there is a major change ==> do not continue
		return g_change;

	/// check other arguments
	for( uint i=0; i<args.size(); i++ ) {
		g_change |= args[i].compare( old.args[i] );	/// all arguments must be the same and in the same order
		if( g_change & 0xFFFF0000 )
			return g_change;
	}


	return g_change;
}





int ARG::compare( const ARG & old ) const
{
	int g_arg_change = 0;
	if( (flags&f_major_change_mask) != (old.flags&f_major_change_mask) )
		return H_FILE::diff_arg_changed_major;
	if( (flags&f_minor_change_mask) != (old.flags&f_minor_change_mask) )
		g_arg_change = H_FILE::diff_arg_changed_minor;

	if( type_remap ) {
		if( ! old.type_remap ) return H_FILE::diff_arg_changed_major;
		g_arg_change |= type_remap->compare( *old.type_remap );
	}
	else if( old.type_remap )
		return H_FILE::diff_arg_changed_major;

	if( g_arg_change & 0xFFFF0000 )
		return g_arg_change;

	charstr tmp = type;
	charstr old_tmp = old.type;

	/// remove argument types that don't matter (e.g. const)
	for( uint i=0; i<STR_skip_args_size; i++ ) {
		remove_word( (char*)tmp.ptr(), STR_skip_args[i] );
		remove_word( (char*)old_tmp.ptr(), STR_skip_args[i] );
	}

    tmp.correct_size();
    old_tmp.correct_size();
    tmp.trim();
    old_tmp.trim();

	//const char * p1 = str_trim( tmp );
	//const char * p2 = str_trim( old_tmp );

	//if( strcmp(p1, p2) )
    if( tmp != old_tmp )
		g_arg_change |= H_FILE::diff_arg_changed;


	return g_arg_change;
}





